/*
 * MODULE  NAME  :
 * PROGRAM NAME  : sbalance.h
 * AUTHOR        : HOTMOCHA
 * CREATE  DATE  : 2015-04-08 22:52:57
 * PROGRAM DESC  :
 *
 * HISTORY       :
 *
 */
#ifndef _H_SBALANCE_H_
#define _H_SBALANCE_H_

#include <netinet/in.h>
#include "sbrbtree.h"
#include <stdio.h>

extern struct sb_cycle_env *g_env;

#define DEBUG   1
#define INFO    2
#define ERROR   3
#define FATAL   4
#define SYSTEM  5
#define SB_LOG_BUF_LEN  1024
extern char sb_log_buf[SB_LOG_BUF_LEN + 1];
extern int sb_log_level;
void GenerateLogMsg(char *filename, int lineno, int log_level);

#define LOGFILE(rule)	((rule)->log_file ? (rule)->log_file : stderr)
#define DebugOutput(format, args...) 						\
	do { 										\
		if (sb_log_level <= DEBUG) {						\
			int __len__ = 0;						\
			GenerateLogMsg(__FILE__, __LINE__, DEBUG); 			\
			__len__ = strlen(sb_log_buf); 					\
			snprintf(sb_log_buf + __len__, SB_LOG_BUF_LEN - __len__, format, ##args); \
			fprintf(g_env->system_log_file, "%s\n", sb_log_buf);            \
		} 									\
	} while(0)

#define InfoOutput(format , args... ) 							\
        do {                                                                            \
                if (sb_log_level <= INFO) {                                            	\
                        int __len__ = 0;                                                \
                        GenerateLogMsg(__FILE__, __LINE__, INFO);                      	\
                        __len__ = strlen(sb_log_buf);                                   \
                        snprintf(sb_log_buf + __len__, SB_LOG_BUF_LEN - __len__, format, ##args); \
			fprintf(g_env->system_log_file, "%s\n", sb_log_buf);            \
                }                                                                       \
        } while(0)

#define ErrorOutput(format , args... )							\
        do {                                                                            \
                if (sb_log_level <= ERROR) {                                            \
                        int __len__ = 0;                                                \
                        GenerateLogMsg(__FILE__, __LINE__, ERROR);                      \
                        __len__ = strlen(sb_log_buf);                                   \
                        snprintf(sb_log_buf + __len__, SB_LOG_BUF_LEN - __len__, format, ##args); \
			fprintf(g_env->system_log_file, "%s\n", sb_log_buf);            \
                }                                                                       \
        } while(0)

#define FatalOutput(format , args... )							\
        do {                                                                            \
		int __len__ = 0;                                                	\
		GenerateLogMsg(__FILE__, __LINE__, FATAL);                      	\
		__len__ = strlen(sb_log_buf);                                   	\
		snprintf(sb_log_buf + __len__, SB_LOG_BUF_LEN - __len__, format, ##args); \
		fprintf(g_env->system_log_file, "%s\n", sb_log_buf);                    \
        } while(0)

#define SystemInfo(format,args... )							\
        do {                                                                            \
		int __len__ = 0;                                                	\
		GenerateLogMsg(__FILE__, __LINE__, SYSTEM);                      	\
		__len__ = strlen(sb_log_buf);                                   	\
		snprintf(sb_log_buf + __len__, SB_LOG_BUF_LEN - __len__, format, ##args); \
		fprintf(g_env->system_log_file, "%s\n", sb_log_buf);                    \
        } while(0)

/*********** log end ****************/
#define BALANCE_MODE_RR			"rr"
#define BALANCE_MODE_RANDOM		"random"
#define BALANCE_MODE_WEIGHT		"weight"
#define BALANCE_MODE_IPHASH		"iphash"	

#define CONF_LISTEN_NUM_PERRULE         5
#define CONF_CLIENT_NUM_PERRULE         50
#define CONF_SERVER_NUM_PERRULE         50
#define CONF_RULE_NAME_MAX_LEN          50
#define CONF_RULE_MAX_NUMS              25
#define CONF_INT_ANY                    -255
#define CONF_STR_ANY                    "any"

struct sb_conf_simple_address
{
	char simple_ip[64+1];
	int ip1;
	int ip2;
	int ip3;
	int ip4;
	int port;
};
/* client address use */
struct sb_conf_range_address
{
	char range_ip[126+1];
	int num;
	int ip1_start, ip1_end;
	int ip2_start, ip2_end;
	int ip3_start, ip3_end;
	int ip4_start, ip4_end;
	int port;		
};

struct sb_conf_server
{
	struct sb_conf_simple_address simple_addr;

	int weight;

	int try_connect;	/* try_connect = 1, when connect failed should wait fail_timeout again */
	int fails;
	int max_fails;
	int fail_timeout;	/* ms */
	struct timeval  find_unhealth_time;	/* find unhealth server's last time */	
};

struct sb_forward_rule 
{
	char rule_name[1 + CONF_RULE_NAME_MAX_LEN];
	char balance_mode[21];

	int listen_num;	
	struct sb_conf_simple_address listen_addr[CONF_LISTEN_NUM_PERRULE];

	int client_num;
	struct sb_conf_range_address client_addr[CONF_CLIENT_NUM_PERRULE];

	int server_num;
	struct sb_conf_server server_addr[CONF_SERVER_NUM_PERRULE];
	int server_total_weight;
	int current_server_index;	/* rr algorithm, current index */

	int connect_timeout;    /* ms scale */
	int keepalive_timeout;  /* ms scale */
	
	/* now no use */
	char log_pathfilename[512];
	FILE *log_file;
};

#define CONNECT_TYPE_CLIENT		1
#define CONNECT_TYPE_SERVER		2

/* according to the fd's use type */
#define EVENT_TYPE_LISTEN		1
#define EVENT_TYPE_SOCKET		2
#define EVENT_TYPE_FILEFD		3

#define MAXIOBUFLEN     16384
#define MAXCANUSEBUFLEN 12288           /* (MAXIOBUFLEN * 3 / 4) */
#define MAXSAFEBUFLEN   4096            /* (MAXIOBUFLEN * 1 / 4) */

#define EMPTY_EVENT_DELAY_TIME	500    /*ms*/

struct sb_cycle_env;
struct sb_event;
struct sb_connection;
struct sb_listen;

struct sb_event_action
{
	int (*init)(struct sb_cycle_env*); 
	int (*done)(struct sb_cycle_env*); 
	int (*add_event)(struct sb_event*, int event);
	int (*del_event)(struct sb_event*, int event);
	int (*add_listen_event)(struct sb_event*);
	int (*del_listen_event)(struct sb_event*);
	int (*process_event)(struct sb_cycle_env*, int timeout, void *arg); 
};

struct sb_net_address
{
	int port;
	char ip[64 + 1];
	struct sockaddr_in sockaddr;
};

typedef int (*sb_event_handler)(struct sb_cycle_env *env, struct sb_event *ev);
struct sb_event
{
	/* can point to sb_connection and sb_listen*/
	void *data;
	sb_event_handler handler;

	unsigned int event_type;	/* listen fd, normal socket fd, file fd ... */

	/* kinds of flag */
	unsigned accept_ready:1;
	unsigned write_ready:1;
	unsigned read_ready:1;
	unsigned active:1;	/* register in event pool, active will be 1, and or not */
	unsigned timeout:1;	/* if the event is timeout set 1 */
	unsigned in_timer_set:1;	/* if the event is set in timer, in_timer_set flag will be 1 */
	unsigned force_delay_read:1;	/* when both side have read event, the first will force the another delete event, after read and send completly
									   add the other's read event */
	unsigned shutdown:1;		/* aready close status, now wait for connection's other side's read or write */
	unsigned error:1;		

        unsigned long timeout_time;  /* ms time */
        struct rb_node node;

	struct sb_event *next;
};

struct sb_listen
{
	int listen_fd;
	int listen_type;	/* listen_type:	manager listen port or normal client listen port */
	struct sb_net_address listen_address;

	struct sb_event listen_event;

	struct sb_forward_rule *forward_rule;

	struct sb_listen *next;	/* no use */
}__attribute__((aligned(sizeof(long))));

struct sb_session;
struct sb_connection
{
	struct sb_net_address net_address;
	int sockfd;
	struct sb_event read;
	struct sb_event write;

	unsigned int connect_type;
	struct sb_session *session;
}__attribute__((aligned(sizeof(long))));


#define FINISH_REASON_SUCESS_TRANSFER		0
#define FINISH_REASON_CLIENTNOTALLOW		1
#define FINISH_REASON_PROCESSEVENTERR		2
#define FINISH_REASON_TIMEOUT			3
#define FINISH_REASON_RESOURCENOTENOUGH		4
#define FINISH_REASON_CLIENTREADERR		5
#define FINISH_REASON_CLIENTWRITEERR		6
#define FINISH_REASON_SERVERREADERR		7
#define FINISH_REASON_SERVERWRITEERR		8
#define FINISH_REASON_CONNECTSERVERERR		9
#define FINISH_REASON_SYSTEMCALLERR		10
#define FINISH_REASON_INTERNALERR		11
#define FINISH_REASON_CANNOTGETSERVER		11
struct sb_session
{
	struct sb_connection client;
	struct sb_connection server;

	char cs_buf[MAXIOBUFLEN];
	unsigned int cs_used;
	char sc_buf[MAXIOBUFLEN];
	unsigned int sc_used;

	unsigned int sessions_type;
	struct sb_session *next;
	struct sb_session *prev;

	unsigned had_failed:1;		/* connect server has two chance, the first failed set had_failed 1 */
	unsigned had_finish:1;		/* had recycle */
	int why_finish;		/* see above struct sb_session */
	struct sb_conf_server *select_server;	
	struct sb_forward_rule *select_rule;
};

#define SYS_MAX_LISTEN_NUM	50	
#define SYS_RECOURCE_POOR_ACCEPT_DELAY	1000	/* ms */
#define SYS_CONFIG_PATHFILE_NAME	256
#define SYS_SIGNAL_CMD_RELOAD_CONFIG	1
#define SYS_SIGNAL_CMD_SHOW_STATUS	2
struct sb_cycle_env
{
	struct sb_session *active_session_list; /* using sessions */
	struct sb_session *reuse_session_list; /* recycle sessions */
	struct sb_session *new_reuse_session_list; /* in the current round get recycle sessions */

	unsigned long active_session_num;
	unsigned long connecting_server_num;
	unsigned long reuse_sessoion_num;
	unsigned long total_success;
	unsigned long total_failed; 

	struct sb_listen listen[SYS_MAX_LISTEN_NUM];
	unsigned int total_listenfd_num;	/* should less than SYS_MAX_LISTEN_NUM */

	/***  config ***/
	int rule_num;
	struct sb_forward_rule* rules[CONF_RULE_MAX_NUMS];
	int accept_delay_timeout;		/* system resource over load, need wait */
	int accept_dalay_flag;
	struct sb_event accept_delay_timer;

	char config_pathfilename[SYS_CONFIG_PATHFILE_NAME + 1];
	
	FILE *system_log_file;
};

extern struct timeval  cache_time;
extern char cache_str_time[sizeof("2014-12-12 12:12:12")];

inline static unsigned long sb_current_msec_time()
{
	unsigned long current_time = cache_time.tv_sec * 1000;
	current_time +=  cache_time.tv_usec / 1000;
	return current_time;
}

inline static unsigned long sb_get_msec_timeout_time(unsigned long timeout)
{
	return sb_current_msec_time() + timeout;
}

#define SB_OK		0
#define SB_ERROR	-1
#define SB_AGAIN	-2
#define SB_DONE		-3
#define SB_DECLINE	-4
#define SB_BUFNOTENOUGH	-5
#define SB_TIMEOUT	-6
#define SB_CONNECTERR	-7

int sb_transfer_read_event(struct sb_cycle_env *env, struct sb_event *e);
int sb_transfer_write_event(struct sb_cycle_env *env, struct sb_event *e);

struct sb_session* sb_new_session(struct sb_cycle_env *env);
void sb_destory_session(struct sb_session *sess);
void sb_list_push_session(struct sb_session **list, struct sb_session *sess);
struct sb_session* sb_list_pop_session(struct sb_session **list);
void sb_list_delete_session(struct sb_session **list, struct sb_session *sess);
void sb_recycle_session(struct sb_cycle_env *env, struct sb_session *sess);
struct sb_session* sb_get_session(struct sb_cycle_env *env);
struct sb_conf_server* sb_load_balance_get_server(struct sb_cycle_env *env, struct sb_forward_rule *rule, char *ip_port);


#endif
